As computer science has evolved, object oriented programming has become one of many familiar models designers and programmers utilize to implement functionality within computer systems. The object model generally is defined by a class structure that includes class members providing both methods and associated data elements belonging to the class. The class members thus provide/define desired functionality within a computer program, wherein an object is declared as an instance of a particular class. As is commonplace, objects often must exchange data and/or invoke other objects operating on the same platform and/or communicate with objects belonging to remote platforms. In order to communicate between objects, interface systems and standards have been developed to define how objects may communicate and/or interact with one another.
A familiar system for communicating and interfacing between objects is known as the Component Object Model (COM), wherein another similar system is referred to as the Common Object Request Brokers Architecture (CORBA). Still yet other communication interfaces may be defined in languages such as JAVA within an operating framework of a Java Virtual Machine, for example. As these and other systems have been developed however, two common object architectures or models generally have emerged and may generally be defined in terms of managed and unmanaged object systems, for example.
Managed objects may be allocated from a heap within a managed software or execution environment and are generally not responsible for managing associated object lifetimes. Managed objects may be described in terms of a data type (e.g., metadata) and automatically collected (e.g., reclaimed) by a managed environment “garbage collector” that removes the object from memory when the object is no longer being accessed. In contrast, unmanaged objects may be allocated from a standard operating system heap, wherein the object itself is responsible for freeing memory it employs when references to the object no longer exist. This may be accomplished through well-known techniques such as reference counting, for example.
Various compilers and other tools expose a managed system's functionality that enable designers to write code that benefits from a managed execution environment. Thus, code that is developed with a language compiler that targets a managed system such as a Common Language Runtime or Java Virtual Machine is referred to as managed code. This type environment employs features such as cross-language integration, cross-language exception handling, enhanced security, versioning and deployment support, a simplified model for component interaction, and debugging and profiling services, for example.
To enable managed systems to provide services for managed code, language compilers typically emit metadata that describes the types, members, and references in managed code. Metadata is stored with the code, wherein a loadable common language runtime portable executable (PE) file contains the metadata. The managed system employs the metadata to locate and load classes, lay out instances in memory, resolve method invocations, generate native code, enforce security, and set run-time context boundaries.
The managed system automatically handles object layout and manages references to objects, releasing them when they are no longer being used. Objects whose lifetimes are managed in this manner are called managed data. As noted above, garbage collection mitigates memory leaks as well as some other common programming errors. If code is managed, designers can use managed data, unmanaged data, or both managed and unmanaged data in a respective application. Since language compilers supply their own types, such as primitive types, systems may not always know (or need to know) whether data is being managed.
A prototypical example whereby runtime code generation is beneficial relates to late bound calls as found in languages such as Visual Basic, ECMAScript, Python, Perl, PHP, Scheme, Ruby, and so forth. In these languages, the types of a receiver and arguments of a method call are not known statically, whereas the managed system method call instructions (e.g., call, callvirt, ldftn, ldvrtftn) require the static types of the method arguments and result. Hence, method dispatch is performed dynamically based on the runtime types of the receiver, actual arguments, and the static name of the method. This interpretative layer is one of the main reasons that late bound languages do not perform as well as statically typed languages.